#pragma once

#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "Logger.h"
#include "Timer.h"

namespace zeroplus::mq
{

    // MessageLoop
    class MessageLoop
    {
    public:
        using Callback = std::function<void()>;
        using Priority = Timer::Priority;

        MessageLoop();

        virtual ~MessageLoop();

        void set_name(const std::string &name);

        const std::string &get_name();

        void register_begin_handler(const Callback &callback);

        void register_end_handler(const Callback &callback);

        void register_idle_handler(const Callback &callback);

        bool run();

        bool async_run();

        bool quit();

        bool wait_for_idle(int ms = Timer::kInfinite, bool check = true);

        bool wait_for_quit(int ms = Timer::kInfinite, bool check = true);

        bool post_task(const Callback &callback, uint16_t priority = Priority::kNormalPriority);

        bool wakeup();

        [[nodiscard]] bool is_running() const;

        [[nodiscard]] bool is_ready_to_quit() const;

        [[nodiscard]] bool is_busy() const;

        [[nodiscard]] size_t get_task_count() const;

    protected:
        [[nodiscard]] virtual size_t get_max_task_count() const;

        [[nodiscard]] virtual size_t get_max_timer_count() const;

        [[nodiscard]] virtual uint32_t get_max_elapsed_time() const; // milliseconds

        [[nodiscard]] virtual bool is_in_same_thread() const;

        virtual void on_begin();

        virtual void on_end();

        virtual void on_idle();

        virtual void on_task_changed(const Callback &callback, uint32_t start_time);

        virtual void on_task_timeout(const Callback &callback, uint32_t elapsed_time);

    private:
        static uint64_t get_current_nano_time();

        bool add_timer(Timer *timer);

        bool remove_timer(Timer *timer);

        void emplace_task(const Callback &callback, uint16_t priority);

        void do_consume();

        void process_timer_task(int64_t &next_sleep_time);

        friend Timer;
        std::unique_ptr<struct MessageLoopImpl> impl_;
        MQ_DISALLOW_COPY_AND_ASSIGN(MessageLoop)
    };

} // namespace zeroplus::mq
